Loading files

rm(list = ls())
graphics.off()

library(DepthProc)
Loading required package: ggplot2
Loading required package: Rcpp
Loading required package: rrcov
Loading required package: robustbase
Scalable Robust Estimators with High Breakdown Point (version 1.7-1)

Loading required package: MASS
Loading required package: np
Nonparametric Kernel Methods for Mixed Datatypes (version 0.60-14)
[vignette("np_faq",package="np") provides answers to frequently asked questions]
[vignette("np",package="np") an overview]
[vignette("entropy_np",package="np") an overview of entropy-based methods]

Attaching package: ‘DepthProc’

The following object is masked from ‘package:base’:

    as.matrix
library(aplpack)

load("~/Documents/thesis/features.Rdata")
load("~/Documents/thesis/lakes.Rdata")
pplot = function(data, x = NULL, n = NULL, col = c("gray"), xlab = "", ylab = "", main = "", mean = FALSE, lwd = c(1), alfa = c(1)){
  library(scales)
  if (length(n) == 0)
    n = dim(data)[1]
  if (length(alfa) == 0)
    alfa = rep(alfa, n)
  if (length(x)==0)
    x = seq(1,dim(data)[2],by=1)
  if (length(col)==1)
    col = rep(col, n)
  if (length(lwd)==1)
    lwd = rep(lwd, n)
  plot(x, data[1,], type = "l", lwd = lwd[1], col = alpha(col[1], alfa[1]), xlab = ylab, ylab = ylab, main = main, ylim = c(min(data), max(data)))
  for (i in 2:n){
    lines(x, data[i,], col = alpha(col[i], alfa[i]), lwd = lwd[i])
  }
  if (mean){
    par(new=TRUE)
    plot(x,apply(data, 2, mean), lwd = lwd[1], type = "l", col = "black", xlab = ylab, ylab = ylab, main = main, ylim = c(min(data), max(data)))
  }
}

f_kass = data.frame("spring.peak" = f_kass[1,], "avg.l1.der" = f_kass[2,], "avg.l1.mean" = f_kass[3,])
f_kort = data.frame("spring.peak" = f_kort[1,], "avg.l1.der" = f_kort[2,], "avg.l1.mean" = f_kort[3,])
f_naut = data.frame("spring.peak" = f_naut[1,], "avg.l1.der" = f_naut[2,], "avg.l1.mean" = f_naut[3,])

Because of the fact that the third variable has a much wider range than the other two, before starting the analysis, the entire datasets are rescaled in [0,1]:

for (j in 1:dim(f_kass)[2]){
  f_kass[,j] = (f_kass[,j] - min(f_kass[,j]))/(max(f_kass[,j])-min(f_kass[,j]))
  f_kort[,j] = (f_kort[,j] - min(f_kort[,j]))/(max(f_kort[,j])-min(f_kort[,j]))
  f_naut[,j] = (f_naut[,j] - min(f_naut[,j]))/(max(f_naut[,j])-min(f_naut[,j]))
}

The features are:

  1. spring.peak: \(max_{t\in[0,1]}f(t)\)
  2. avg.l1.der: \(\int_{0}^1 |f^\prime(t)|dt\)
  3. avg.l1.mean: \(\int_{0}^1 |f(t) - \mu_{lake}(t)|dt\)

Kass

d = f_kass
bagplot_ = bagplot.pairs(d)

the number of outliers is very low:

par(mfrow=c(1,4))
boxplot(d)
hist(d$spring.peak)
hist(d$avg.l1.der)
hist(d$avg.l1.mean)

We now perform PCA (on the rescaled dataset). The first PC explains the majority of the variability:

pc.data <- princomp(d, scores=T)
# summary(pc.data)
par(mfcol = c(1,3))
load.data <- pc.data$loadings
for(i in 1:3) barplot(load.data[,i], ylim = c(-1, 1), main=paste("PC",i))

The first PC is a mean of the 3 features, while the second one seems to highlight a contrast between the presence of a high spring peak and the overall “dissimilarity” of the curve wrt to the mean function.

layout(matrix(c(2,3,1,3),2,byrow=T))
plot(pc.data, las=2, main='Principal Components')
abline(h=1, col='blue')
barplot(sapply(as.data.frame(d),sd)^2, las=2, main='Original Variables', ylab='Variances')
plot(cumsum(pc.data$sde^2)/sum(pc.data$sde^2), type='b', axes=F, xlab='Number of components', ylab='Contribution to the total variance', ylim=c(0,1))
abline(h=1, col='blue')
abline(h=0.9, lty=2, col='blue')
box()
axis(2,at=0:10/10,labels=0:10/10)
par(mfrow = c(1,1))

For future analysis, we could simplify this dataset by only focusing on the first 2 PC (considering also the fact that the features are highly correlated):

plot(pc.data$scores[,1], pc.data$scores[,2], pch = 19, xlab = "PC1", ylab = "PC2")

It seems like most years have a low PC1 and a high PC2. Why is that?

plot(pc.data$scores[,1], pc.data$scores[,2], pch = 19)
pc1.weird = which(pc.data$scores[,1]>0.7)
pc2.weird = which(pc.data$scores[,2]< -0.3)
points(pc.data$scores[pc1.weird, 1],
       pc.data$scores[pc1.weird, 2], col = "red", pch = 19)
points(pc.data$scores[pc2.weird, 1],
       pc.data$scores[pc2.weird, 2], col = "green", pch = 19)

Lets plot the functions which have a high/low PC1:

lake = kass
d_lake = d_kass
d = f_kass

color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.1, dim(lake)[1])
color[pc1.weird] = "red"
lwd[pc1.weird] = 2
alfa[pc1.weird] = 1
pplot(lake, col = color, main = "High PC1", lwd = lwd, alfa = alfa)

pc1.weird = which(pc.data$scores[,1]< -0.35)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.1, dim(lake)[1])
color[pc1.weird] = "red"
lwd[pc1.weird] = 2
alfa[pc1.weird] = 1
pplot(lake, col = color, main = "Low PC1", lwd = lwd, alfa = alfa)

A high PC1 corresponds to functions whose oscillations throughout the year are particularly high (in absolute value), i.e. years in which the previous winter was particularly warm and there were many heavy rains in autumn. If the first PC is low, then the signals are almost constant (previous winter was warm and there were almost no rains). Lets plot the functions which have a low PC2:

color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.3, dim(lake)[1])
color[pc2.weird] = "red"
lwd[pc2.weird] = 2
alfa[pc2.weird] = 1
pplot(lake, col = color, main = "Low PC2", lwd = lwd, alfa = alfa)

A low PC2 means that the first peak (spring peak) is actually quite low (cold winters), but the signals have quite high peaks during the winter (due possibly to heavy rains). The vast majority of the years, have a high PC2:

pc2.weird = which(pc.data$scores[,2]> 0.15)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.1, dim(lake)[1])
color[pc2.weird] = "red"
lwd[pc2.weird] = 2
alfa[pc2.weird] = 1
pplot(lake, col = color, main = "High PC2", lwd = lwd, alfa = alfa)

Indeed, these years are very similar to the mean.

In short: - PC1 talks about peakS intensities: the higher, the more intense the peakS (also the ones in the end of the year); the lower, the more constant the overall signal. - PC2 talks about the autumn/winter of the current year: if low then the year has many autumn peaks, if high then the function is very similar to the mean, hence it has only one peak (which happens during the spring).

Kort

Performing the same analysis on the Kort dataset, the bagplots highlight the presence of many more ourliers (due to the fact that the distribution of the 3 features seems much more concentrated) and the correlation among spring.peak and avg.l1.der seems to be even more clear. As with Kass, the first 2 PC explain the vast majority of the variability and their interpretation is the same of Kass (PC1: average of the 3 features and PC2: contrast between spring peak and average displacement from the mean). In this case, the plot of the 2 first PC seems to be sufficient to embed the dataset:

lake = kort
d_lake = d_kort
d = f_kort
plot(princomp(f_kort, scores=T)$scores[,1], princomp(f_kort, scores=T)$scores[,2], pch = 19)

pc.data <- princomp(f_kort, scores=T)
par(mfrow = c(1,1))
plot(pc.data$scores[,1], pc.data$scores[,2], pch = 19)
pc1.weird = which(pc.data$scores[,1]>0.6)
pc2.weird = which(pc.data$scores[,2]< -0.15)
points(pc.data$scores[pc1.weird, 1],
       pc.data$scores[pc1.weird, 2], col = "red", pch = 19)
points(pc.data$scores[pc2.weird, 1],
       pc.data$scores[pc2.weird, 2], col = "green", pch = 19)

color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(1.5, dim(lake)[1])
color[pc1.weird] = "red"
lwd[pc1.weird] = 1
alfa[pc1.weird] = 1
pplot(lake, col = color, main = "High PC1", lwd = lwd, alfa = alfa)

pc1.weird = which(pc.data$scores[,1]< - 0.15)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.2, dim(lake)[1])
color[pc1.weird] = "red"
lwd[pc1.weird] = 1
alfa[pc1.weird] = 1
pplot(lake, col = color, main = "Low PC1", lwd = lwd, alfa = alfa)

PC1 has a similar interpretation when compared with Kass: if PC1 is low, then the funcitons are almost constant, while if high, they have quite high spring peaks. Unfortunately, the link between PC1 and the autumn peaks seems to be lost, indeed the role of the aveage abs of variability (first derivative) is not that relevant in Kort’s PC1 when compared with Kass:

par(mfcol = c(1,2))
barplot(princomp(f_kass, scores=T)$loadings[,1], ylim = c(-1, 1), main="PC1 - Kass")
barplot(princomp(f_kort, scores=T)$loadings[,1], ylim = c(-1, 1), main="PC1 - Kort")

About PC2:

pc2.weird = which(pc.data$scores[,2]> 0.1)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.3, dim(lake)[1])
color[pc2.weird] = "red"
lwd[pc2.weird] = 2
alfa[pc2.weird] = 1
pplot(lake, col = color, main = "High PC2", lwd = lwd, alfa = alfa)

pc2.weird = which(pc.data$scores[,2]< -0.15)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.3, dim(lake)[1])
color[pc2.weird] = "red"
lwd[pc2.weird] = 2
alfa[pc2.weird] = 1
pplot(lake, col = color, main = "Low PC2", lwd = lwd, alfa = alfa)

High PC2: then the curve has mainly one peak which happens during spring (some exeptions present prominent peaks even during autumn and winter). Low PC2: much more difficult to interpret, but rememer that the second PC explains much less variability than PC1…

Naut

The fact that the data collection process of Kort and Naut is different from the one of Kass may be the reason for the similarities among the 2 finnish lakes when compared with the swedish one. Even the scatterplot of PC1 and PC2 are similar:

lake = naut
d_lake = d_naut
d = f_naut

pc.data <- princomp(f_naut, scores=T)
par(mfrow = c(1,1))
plot(princomp(f_naut, scores=T)$scores[,1], princomp(f_naut, scores=T)$scores[,2], pch = 19)

The barplot of the loadings of the PCA for naut is very similar to Kass. Let’s look at PC1:

pc1.weird = which(pc.data$scores[,1]> 0.5)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.2, dim(lake)[1])
color[pc1.weird] = "red"
lwd[pc1.weird] = 1
alfa[pc1.weird] = 1
pplot(lake, col = color, main = "High PC1", lwd = lwd, alfa = alfa)

pc1.weird = which(pc.data$scores[,1]< -0.19)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.1, dim(lake)[1])
color[pc1.weird] = "red"
lwd[pc1.weird] = 2
alfa[pc1.weird] = 1
pplot(lake, col = color, main = "Low PC1", lwd = lwd, alfa = alfa)

PC1 has the exact same interpretation as lake Kass, contrary to Kort… Let’s look at PC2:

pc2.weird = which(pc.data$scores[,2]< -0.15)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.3, dim(lake)[1])
color[pc2.weird] = "red"
lwd[pc2.weird] = 2
alfa[pc2.weird] = 1
pplot(lake, col = color, main = "Low PC2", lwd = lwd, alfa = alfa)

pc2.weird = which(pc.data$scores[,2]> 0.09)
color = rep("gray", dim(lake)[1])
lwd = rep(1, dim(lake)[1])
alfa = rep(0.3, dim(lake)[1])
color[pc2.weird] = "red"
lwd[pc2.weird] = 2
alfa[pc2.weird] = 1
pplot(lake, col = color, main = "High PC2", lwd = lwd, alfa = alfa)

PC2 has a similar interpretation to PC2 of Kass, but again it is less evident.

Overall: * PC1: peaks intensity (the lower, the more constant; the higher, the more evident the peaks are) * PC2: (the higher, the more a function resembles the mean; the lower, the more shifted the spring peak is)

CONSIDERING THAT KORT AND NAUT ARE MUCH MORE “WIGGLY” THAN KASS AND THAT THE SECOND PC EXPLINS MUCH LESS VARIABILITY THAN THE FIRST, WE STICK WITH THE INTERPRETATION OF PC1 AND PCS BASED ON KASS. MOREOVER, PC2 OF KASS EXPLAINS MORE VARIABILITY THAN PC2 OF KORT AND NAUT:

par(mfrow=c(1,1))
plot(cumsum(princomp(f_kass, scores=T)$sde^2)/sum(princomp(f_kass, scores=T)$sde^2), type='b', axes=F, xlab='Number of components', col = "black", ylab='Contribution to the total variance', ylim=c(0,1))
points(cumsum(princomp(f_kort, scores=T)$sde^2)/sum(princomp(f_kort, scores=T)$sde^2), type='b', xlab='Number of components', col = "blue")
points(cumsum(princomp(f_naut, scores=T)$sde^2)/sum(princomp(f_naut, scores=T)$sde^2), type='b', xlab='Number of components', col = "red")
axis(2,at=0:10/10,labels=0:10/10)
legend(1,0.5, legend=c("kass", "kort", "naut"), col = c("black", "blue", "red"), lty = 1)
box()

HC

lake = kass
d_lake = d_kass
d = f_kass
pc.data <- princomp(d, scores=T)
dd = data.frame("PC1"=pc.data$scores[,1],"PC2"=pc.data$scores[,2])
d <- dist(dd, method = 'euclidean')
hc <- hclust(d, method = "average")
plot(hc, labels=FALSE, xlab="", sub="")
rect.hclust(hc, k = 4, border = "red")
rect.hclust(hc, k = 5, border = "green")
rect.hclust(hc, k = 6, border = "blue")

# table(cutree(hc, k = 3))

By applying hclust, we see that the results are not very satisfying, since the goal would be to cluster climate types and already with k = 3 we see that one cluster is quite scarce (less than 60 years belong to it). Moreover the clustering is completely driven by the first PC:

par(mfrow = c(2,2))
plot(dd, col = cutree(hc, k = 3), main = "k = 3")
plot(dd, col = cutree(hc, k = 4), main = "k = 4")
plot(dd, col = cutree(hc, k = 5), main = "k = 5")
plot(dd, col = cutree(hc, k = 6), main = "k = 6")
par(mfrow = c(1,1))

Exactly the same results hold for the other 2 finnish lakes.

ConfromalClustering

Very bad

K-means

[The following results are applied to Kass, but they can be generalized to the finnish lakes] By applying k-means with k = 3, we see a clar improvement when compared to HC (with 3 groups):

lake = kass
d_lake = d_kass
d = f_kass
pc.data <- princomp(d, scores=T)
dd = data.frame("PC1"=pc.data$scores[,1],"PC2"=pc.data$scores[,2])
km = kmeans(dd, centers = 3, nstart = 20)
table(km$cluster)

   1    2    3 
2209 2060 1002 

Still, PC1 seems to be the most relevant component:

par(mfrow = c(3,2))
plot(dd, col = kmeans(dd, centers = 3, nstart = 20)$cluster, main = "k = 3")
plot(dd, col = kmeans(dd, centers = 4, nstart = 20)$cluster, main = "k = 4")
plot(dd, col = kmeans(dd, centers = 5, nstart = 20)$cluster, main = "k = 5")
plot(dd, col = kmeans(dd, centers = 6, nstart = 20)$cluster, main = "k = 6")
plot(dd, col = kmeans(dd, centers = 7, nstart = 20)$cluster, main = "k = 7")
plot(dd, col = kmeans(dd, centers = 8, nstart = 20)$cluster, main = "k = 8")
par(mfrow = c(1,1))

If we tried to include the third PC in the analysis, we would obtain slighlty different results:

ddd = dd
ddd$PC3 = pc.data$scores[,3]
par(mfrow = c(3,2))
plot(ddd, col = kmeans(ddd, centers = 3, nstart = 20)$cluster, main = "k = 3")

plot(ddd, col = kmeans(ddd, centers = 4, nstart = 20)$cluster, main = "k = 4")

plot(ddd, col = kmeans(ddd, centers = 5, nstart = 20)$cluster, main = "k = 5")

plot(ddd, col = kmeans(ddd, centers = 6, nstart = 20)$cluster, main = "k = 6")

plot(ddd, col = kmeans(ddd, centers = 7, nstart = 20)$cluster, main = "k = 7")

plot(ddd, col = kmeans(ddd, centers = 8, nstart = 20)$cluster, main = "k = 8")
par(mfrow = c(1,1))

But still PC1 is the driving factor and, moreover, by considering 3 PC we are not really performing dimensionality reduction. Moreover, the 3rd PC always explains a very low amount of variability:

summary(pc.data)
Importance of components:
                          Comp.1    Comp.2     Comp.3
Standard deviation     0.2421391 0.0934147 0.05203741
Proportion of Variance 0.8368071 0.1245449 0.03864798
Cumulative Proportion  0.8368071 0.9613520 1.00000000

For this reason, we decide not to include PC3 in the analysis. Finally, clearly Kmeans is more satisfying than hclust unless k is small (roughly < 6).

BaggingVoronoi:

K = 3, L = 100, B = 500 (2 minutes)

K = 6, L = 10, B = 500 (4 minutes)

LS0tCnRpdGxlOiAiTXV0bGl2YXJpYXRlIGFuYWx5c2lzIG9mIGxha2UgS2Fzc2rDtm4sIEtvcnR0YWrDpHJ2aSBhbmQgTmF1dGFqw6RydmkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiFbXSgvVXNlcnMvZGlzYS9EZXNrdG9wL1NjcmVlbnNob3QzLnBuZykKCiMgTG9hZGluZyBmaWxlcwpgYGB7cn0Kcm0obGlzdCA9IGxzKCkpCmdyYXBoaWNzLm9mZigpCgpsaWJyYXJ5KERlcHRoUHJvYykKbGlicmFyeShhcGxwYWNrKQoKbG9hZCgifi9Eb2N1bWVudHMvdGhlc2lzL2ZlYXR1cmVzLlJkYXRhIikKbG9hZCgifi9Eb2N1bWVudHMvdGhlc2lzL2xha2VzLlJkYXRhIikKcHBsb3QgPSBmdW5jdGlvbihkYXRhLCB4ID0gTlVMTCwgbiA9IE5VTEwsIGNvbCA9IGMoImdyYXkiKSwgeGxhYiA9ICIiLCB5bGFiID0gIiIsIG1haW4gPSAiIiwgbWVhbiA9IEZBTFNFLCBsd2QgPSBjKDEpLCBhbGZhID0gYygxKSl7CiAgbGlicmFyeShzY2FsZXMpCiAgaWYgKGxlbmd0aChuKSA9PSAwKQogICAgbiA9IGRpbShkYXRhKVsxXQogIGlmIChsZW5ndGgoYWxmYSkgPT0gMCkKICAgIGFsZmEgPSByZXAoYWxmYSwgbikKICBpZiAobGVuZ3RoKHgpPT0wKQogICAgeCA9IHNlcSgxLGRpbShkYXRhKVsyXSxieT0xKQogIGlmIChsZW5ndGgoY29sKT09MSkKICAgIGNvbCA9IHJlcChjb2wsIG4pCiAgaWYgKGxlbmd0aChsd2QpPT0xKQogICAgbHdkID0gcmVwKGx3ZCwgbikKICBwbG90KHgsIGRhdGFbMSxdLCB0eXBlID0gImwiLCBsd2QgPSBsd2RbMV0sIGNvbCA9IGFscGhhKGNvbFsxXSwgYWxmYVsxXSksIHhsYWIgPSB5bGFiLCB5bGFiID0geWxhYiwgbWFpbiA9IG1haW4sIHlsaW0gPSBjKG1pbihkYXRhKSwgbWF4KGRhdGEpKSkKICBmb3IgKGkgaW4gMjpuKXsKICAgIGxpbmVzKHgsIGRhdGFbaSxdLCBjb2wgPSBhbHBoYShjb2xbaV0sIGFsZmFbaV0pLCBsd2QgPSBsd2RbaV0pCiAgfQogIGlmIChtZWFuKXsKICAgIHBhcihuZXc9VFJVRSkKICAgIHBsb3QoeCxhcHBseShkYXRhLCAyLCBtZWFuKSwgbHdkID0gbHdkWzFdLCB0eXBlID0gImwiLCBjb2wgPSAiYmxhY2siLCB4bGFiID0geWxhYiwgeWxhYiA9IHlsYWIsIG1haW4gPSBtYWluLCB5bGltID0gYyhtaW4oZGF0YSksIG1heChkYXRhKSkpCiAgfQp9CgpmX2thc3MgPSBkYXRhLmZyYW1lKCJzcHJpbmcucGVhayIgPSBmX2thc3NbMSxdLCAiYXZnLmwxLmRlciIgPSBmX2thc3NbMixdLCAiYXZnLmwxLm1lYW4iID0gZl9rYXNzWzMsXSkKZl9rb3J0ID0gZGF0YS5mcmFtZSgic3ByaW5nLnBlYWsiID0gZl9rb3J0WzEsXSwgImF2Zy5sMS5kZXIiID0gZl9rb3J0WzIsXSwgImF2Zy5sMS5tZWFuIiA9IGZfa29ydFszLF0pCmZfbmF1dCA9IGRhdGEuZnJhbWUoInNwcmluZy5wZWFrIiA9IGZfbmF1dFsxLF0sICJhdmcubDEuZGVyIiA9IGZfbmF1dFsyLF0sICJhdmcubDEubWVhbiIgPSBmX25hdXRbMyxdKQoKYGBgCkJlY2F1c2Ugb2YgdGhlIGZhY3QgdGhhdCB0aGUgdGhpcmQgdmFyaWFibGUgaGFzIGEgbXVjaCB3aWRlciByYW5nZSB0aGFuIHRoZSBvdGhlciB0d28sIGJlZm9yZSBzdGFydGluZyB0aGUgYW5hbHlzaXMsIHRoZSBlbnRpcmUgZGF0YXNldHMgYXJlIHJlc2NhbGVkIGluIFswLDFdOgpgYGB7cn0KZm9yIChqIGluIDE6ZGltKGZfa2FzcylbMl0pewogIGZfa2Fzc1ssal0gPSAoZl9rYXNzWyxqXSAtIG1pbihmX2thc3NbLGpdKSkvKG1heChmX2thc3NbLGpdKS1taW4oZl9rYXNzWyxqXSkpCiAgZl9rb3J0WyxqXSA9IChmX2tvcnRbLGpdIC0gbWluKGZfa29ydFssal0pKS8obWF4KGZfa29ydFssal0pLW1pbihmX2tvcnRbLGpdKSkKICBmX25hdXRbLGpdID0gKGZfbmF1dFssal0gLSBtaW4oZl9uYXV0WyxqXSkpLyhtYXgoZl9uYXV0WyxqXSktbWluKGZfbmF1dFssal0pKQp9CmBgYApUaGUgZmVhdHVyZXMgYXJlOgoKMS4gc3ByaW5nLnBlYWs6ICRtYXhfe3RcaW5bMCwxXX1mKHQpJAoyLiBhdmcubDEuZGVyOiAkXGludF97MH1eMSB8Zl5ccHJpbWUodCl8ZHQkCjMuIGF2Zy5sMS5tZWFuOiAkXGludF97MH1eMSB8Zih0KSAtIFxtdV97bGFrZX0odCl8ZHQkCgojIyBLYXNzCmBgYHtyfQpkID0gZl9rYXNzCmJhZ3Bsb3RfID0gYmFncGxvdC5wYWlycyhkKQpgYGAKdGhlIG51bWJlciBvZiBvdXRsaWVycyBpcyB2ZXJ5IGxvdzoKYGBge3J9CnBhcihtZnJvdz1jKDEsNCkpCmJveHBsb3QoZCkKaGlzdChkJHNwcmluZy5wZWFrKQpoaXN0KGQkYXZnLmwxLmRlcikKaGlzdChkJGF2Zy5sMS5tZWFuKQpgYGAKV2Ugbm93IHBlcmZvcm0gUENBIChvbiB0aGUgcmVzY2FsZWQgZGF0YXNldCkuIFRoZSBmaXJzdCBQQyBleHBsYWlucyB0aGUgbWFqb3JpdHkgb2YgdGhlIHZhcmlhYmlsaXR5OgpgYGB7cn0KcGMuZGF0YSA8LSBwcmluY29tcChkLCBzY29yZXM9VCkKIyBzdW1tYXJ5KHBjLmRhdGEpCnBhcihtZmNvbCA9IGMoMSwzKSkKbG9hZC5kYXRhIDwtIHBjLmRhdGEkbG9hZGluZ3MKZm9yKGkgaW4gMTozKSBiYXJwbG90KGxvYWQuZGF0YVssaV0sIHlsaW0gPSBjKC0xLCAxKSwgbWFpbj1wYXN0ZSgiUEMiLGkpKQpgYGAKVGhlIGZpcnN0IFBDIGlzIGEgbWVhbiBvZiB0aGUgMyBmZWF0dXJlcywgd2hpbGUgdGhlIHNlY29uZCBvbmUgc2VlbXMgdG8gaGlnaGxpZ2h0IGEgY29udHJhc3QgYmV0d2VlbiB0aGUgcHJlc2VuY2Ugb2YgYSBoaWdoIHNwcmluZyBwZWFrIGFuZCB0aGUgb3ZlcmFsbCAiZGlzc2ltaWxhcml0eSIgb2YgdGhlIGN1cnZlIHdydCB0byB0aGUgbWVhbiBmdW5jdGlvbi4gCmBgYHtyfQpsYXlvdXQobWF0cml4KGMoMiwzLDEsMyksMixieXJvdz1UKSkKcGxvdChwYy5kYXRhLCBsYXM9MiwgbWFpbj0nUHJpbmNpcGFsIENvbXBvbmVudHMnKQphYmxpbmUoaD0xLCBjb2w9J2JsdWUnKQpiYXJwbG90KHNhcHBseShhcy5kYXRhLmZyYW1lKGQpLHNkKV4yLCBsYXM9MiwgbWFpbj0nT3JpZ2luYWwgVmFyaWFibGVzJywgeWxhYj0nVmFyaWFuY2VzJykKcGxvdChjdW1zdW0ocGMuZGF0YSRzZGVeMikvc3VtKHBjLmRhdGEkc2RlXjIpLCB0eXBlPSdiJywgYXhlcz1GLCB4bGFiPSdOdW1iZXIgb2YgY29tcG9uZW50cycsIHlsYWI9J0NvbnRyaWJ1dGlvbiB0byB0aGUgdG90YWwgdmFyaWFuY2UnLCB5bGltPWMoMCwxKSkKYWJsaW5lKGg9MSwgY29sPSdibHVlJykKYWJsaW5lKGg9MC45LCBsdHk9MiwgY29sPSdibHVlJykKYm94KCkKYXhpcygyLGF0PTA6MTAvMTAsbGFiZWxzPTA6MTAvMTApCnBhcihtZnJvdyA9IGMoMSwxKSkKYGBgCkZvciBmdXR1cmUgYW5hbHlzaXMsIHdlIGNvdWxkIHNpbXBsaWZ5IHRoaXMgZGF0YXNldCBieSBvbmx5IGZvY3VzaW5nIG9uIHRoZSBmaXJzdCAyIFBDIChjb25zaWRlcmluZyBhbHNvIHRoZSBmYWN0IHRoYXQgdGhlIGZlYXR1cmVzIGFyZSBoaWdobHkgY29ycmVsYXRlZCk6CmBgYHtyfQpwbG90KHBjLmRhdGEkc2NvcmVzWywxXSwgcGMuZGF0YSRzY29yZXNbLDJdLCBwY2ggPSAxOSwgeGxhYiA9ICJQQzEiLCB5bGFiID0gIlBDMiIpCmBgYApJdCBzZWVtcyBsaWtlIG1vc3QgeWVhcnMgaGF2ZSBhIGxvdyBQQzEgYW5kIGEgaGlnaCBQQzIuIFdoeSBpcyB0aGF0PwpgYGB7cn0KcGxvdChwYy5kYXRhJHNjb3Jlc1ssMV0sIHBjLmRhdGEkc2NvcmVzWywyXSwgcGNoID0gMTkpCnBjMS53ZWlyZCA9IHdoaWNoKHBjLmRhdGEkc2NvcmVzWywxXT4wLjcpCnBjMi53ZWlyZCA9IHdoaWNoKHBjLmRhdGEkc2NvcmVzWywyXTwgLTAuMykKcG9pbnRzKHBjLmRhdGEkc2NvcmVzW3BjMS53ZWlyZCwgMV0sCiAgICAgICBwYy5kYXRhJHNjb3Jlc1twYzEud2VpcmQsIDJdLCBjb2wgPSAicmVkIiwgcGNoID0gMTkpCnBvaW50cyhwYy5kYXRhJHNjb3Jlc1twYzIud2VpcmQsIDFdLAogICAgICAgcGMuZGF0YSRzY29yZXNbcGMyLndlaXJkLCAyXSwgY29sID0gImdyZWVuIiwgcGNoID0gMTkpCmBgYApMZXRzIHBsb3QgdGhlIGZ1bmN0aW9ucyB3aGljaCBoYXZlIGEgaGlnaC9sb3cgUEMxOgpgYGB7cn0KbGFrZSA9IGthc3MKZF9sYWtlID0gZF9rYXNzCmQgPSBmX2thc3MKCmNvbG9yID0gcmVwKCJncmF5IiwgZGltKGxha2UpWzFdKQpsd2QgPSByZXAoMSwgZGltKGxha2UpWzFdKQphbGZhID0gcmVwKDAuMSwgZGltKGxha2UpWzFdKQpjb2xvcltwYzEud2VpcmRdID0gInJlZCIKbHdkW3BjMS53ZWlyZF0gPSAyCmFsZmFbcGMxLndlaXJkXSA9IDEKcHBsb3QobGFrZSwgY29sID0gY29sb3IsIG1haW4gPSAiSGlnaCBQQzEiLCBsd2QgPSBsd2QsIGFsZmEgPSBhbGZhKQpgYGAKYGBge3J9CnBjMS53ZWlyZCA9IHdoaWNoKHBjLmRhdGEkc2NvcmVzWywxXTwgLTAuMzUpCmNvbG9yID0gcmVwKCJncmF5IiwgZGltKGxha2UpWzFdKQpsd2QgPSByZXAoMSwgZGltKGxha2UpWzFdKQphbGZhID0gcmVwKDAuMSwgZGltKGxha2UpWzFdKQpjb2xvcltwYzEud2VpcmRdID0gInJlZCIKbHdkW3BjMS53ZWlyZF0gPSAyCmFsZmFbcGMxLndlaXJkXSA9IDEKcHBsb3QobGFrZSwgY29sID0gY29sb3IsIG1haW4gPSAiTG93IFBDMSIsIGx3ZCA9IGx3ZCwgYWxmYSA9IGFsZmEpCmBgYApBIGhpZ2ggUEMxIGNvcnJlc3BvbmRzIHRvIGZ1bmN0aW9ucyB3aG9zZSBvc2NpbGxhdGlvbnMgdGhyb3VnaG91dCB0aGUgeWVhciBhcmUgcGFydGljdWxhcmx5IGhpZ2ggKGluIGFic29sdXRlIHZhbHVlKSwgaS5lLiB5ZWFycyBpbiB3aGljaCB0aGUgcHJldmlvdXMgd2ludGVyIHdhcyBwYXJ0aWN1bGFybHkgd2FybSBhbmQgdGhlcmUgd2VyZSBtYW55IGhlYXZ5IHJhaW5zIGluIGF1dHVtbi4gSWYgdGhlIGZpcnN0IFBDIGlzIGxvdywgdGhlbiB0aGUgc2lnbmFscyBhcmUgYWxtb3N0IGNvbnN0YW50IChwcmV2aW91cyB3aW50ZXIgd2FzIHdhcm0gYW5kIHRoZXJlIHdlcmUgYWxtb3N0IG5vIHJhaW5zKS4KTGV0cyBwbG90IHRoZSBmdW5jdGlvbnMgd2hpY2ggaGF2ZSBhIGxvdyBQQzI6CmBgYHtyfQpjb2xvciA9IHJlcCgiZ3JheSIsIGRpbShsYWtlKVsxXSkKbHdkID0gcmVwKDEsIGRpbShsYWtlKVsxXSkKYWxmYSA9IHJlcCgwLjMsIGRpbShsYWtlKVsxXSkKY29sb3JbcGMyLndlaXJkXSA9ICJyZWQiCmx3ZFtwYzIud2VpcmRdID0gMgphbGZhW3BjMi53ZWlyZF0gPSAxCnBwbG90KGxha2UsIGNvbCA9IGNvbG9yLCBtYWluID0gIkxvdyBQQzIiLCBsd2QgPSBsd2QsIGFsZmEgPSBhbGZhKQpgYGAKQSBsb3cgUEMyIG1lYW5zIHRoYXQgdGhlIGZpcnN0IHBlYWsgKHNwcmluZyBwZWFrKSBpcyBhY3R1YWxseSBxdWl0ZSBsb3cgKGNvbGQgd2ludGVycyksIGJ1dCB0aGUgc2lnbmFscyBoYXZlIHF1aXRlIGhpZ2ggcGVha3MgZHVyaW5nIHRoZSB3aW50ZXIgKGR1ZSBwb3NzaWJseSB0byBoZWF2eSByYWlucykuIFRoZSB2YXN0IG1ham9yaXR5IG9mIHRoZSB5ZWFycywgaGF2ZSBhIGhpZ2ggUEMyOgpgYGB7cn0KcGMyLndlaXJkID0gd2hpY2gocGMuZGF0YSRzY29yZXNbLDJdPiAwLjE1KQpjb2xvciA9IHJlcCgiZ3JheSIsIGRpbShsYWtlKVsxXSkKbHdkID0gcmVwKDEsIGRpbShsYWtlKVsxXSkKYWxmYSA9IHJlcCgwLjEsIGRpbShsYWtlKVsxXSkKY29sb3JbcGMyLndlaXJkXSA9ICJyZWQiCmx3ZFtwYzIud2VpcmRdID0gMgphbGZhW3BjMi53ZWlyZF0gPSAxCnBwbG90KGxha2UsIGNvbCA9IGNvbG9yLCBtYWluID0gIkhpZ2ggUEMyIiwgbHdkID0gbHdkLCBhbGZhID0gYWxmYSkKYGBgCkluZGVlZCwgdGhlc2UgeWVhcnMgYXJlIHZlcnkgc2ltaWxhciB0byB0aGUgbWVhbi4KCkluIHNob3J0OiAKLSBQQzEgdGFsa3MgYWJvdXQgcGVha1MgaW50ZW5zaXRpZXM6IHRoZSBoaWdoZXIsIHRoZSBtb3JlIGludGVuc2UgdGhlIHBlYWtTIChhbHNvIHRoZSBvbmVzIGluIHRoZSBlbmQgb2YgdGhlIHllYXIpOyB0aGUgbG93ZXIsIHRoZSBtb3JlIGNvbnN0YW50IHRoZSBvdmVyYWxsIHNpZ25hbC4KLSBQQzIgdGFsa3MgYWJvdXQgdGhlIGF1dHVtbi93aW50ZXIgb2YgdGhlIGN1cnJlbnQgeWVhcjogaWYgbG93IHRoZW4gdGhlIHllYXIgaGFzIG1hbnkgYXV0dW1uIHBlYWtzLCBpZiBoaWdoIHRoZW4gdGhlIGZ1bmN0aW9uIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgbWVhbiwgaGVuY2UgaXQgaGFzIG9ubHkgb25lIHBlYWsgKHdoaWNoIGhhcHBlbnMgZHVyaW5nIHRoZSBzcHJpbmcpLgoKIyMgS29ydApQZXJmb3JtaW5nIHRoZSBzYW1lIGFuYWx5c2lzIG9uIHRoZSBLb3J0IGRhdGFzZXQsIHRoZSBiYWdwbG90cyBoaWdobGlnaHQgdGhlIHByZXNlbmNlIG9mIG1hbnkgbW9yZSBvdXJsaWVycyAoZHVlIHRvIHRoZSBmYWN0IHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgMyBmZWF0dXJlcyBzZWVtcyBtdWNoIG1vcmUgY29uY2VudHJhdGVkKSBhbmQgdGhlIGNvcnJlbGF0aW9uIGFtb25nIHNwcmluZy5wZWFrIGFuZCBhdmcubDEuZGVyIHNlZW1zIHRvIGJlIGV2ZW4gbW9yZSBjbGVhci4KQXMgd2l0aCBLYXNzLCB0aGUgZmlyc3QgMiBQQyBleHBsYWluIHRoZSB2YXN0IG1ham9yaXR5IG9mIHRoZSB2YXJpYWJpbGl0eSBhbmQgdGhlaXIgaW50ZXJwcmV0YXRpb24gaXMgdGhlIHNhbWUgb2YgS2FzcyAoUEMxOiBhdmVyYWdlIG9mIHRoZSAzIGZlYXR1cmVzIGFuZCBQQzI6IGNvbnRyYXN0IGJldHdlZW4gc3ByaW5nIHBlYWsgYW5kIGF2ZXJhZ2UgZGlzcGxhY2VtZW50IGZyb20gdGhlIG1lYW4pLiBJbiB0aGlzIGNhc2UsIHRoZSBwbG90IG9mIHRoZSAyIGZpcnN0IFBDIHNlZW1zIHRvIGJlIHN1ZmZpY2llbnQgdG8gZW1iZWQgdGhlIGRhdGFzZXQ6CmBgYHtyfQpsYWtlID0ga29ydApkX2xha2UgPSBkX2tvcnQKZCA9IGZfa29ydApwbG90KHByaW5jb21wKGZfa29ydCwgc2NvcmVzPVQpJHNjb3Jlc1ssMV0sIHByaW5jb21wKGZfa29ydCwgc2NvcmVzPVQpJHNjb3Jlc1ssMl0sIHBjaCA9IDE5KQpgYGAKYGBge3J9CnBjLmRhdGEgPC0gcHJpbmNvbXAoZl9rb3J0LCBzY29yZXM9VCkKcGFyKG1mcm93ID0gYygxLDEpKQpwbG90KHBjLmRhdGEkc2NvcmVzWywxXSwgcGMuZGF0YSRzY29yZXNbLDJdLCBwY2ggPSAxOSkKcGMxLndlaXJkID0gd2hpY2gocGMuZGF0YSRzY29yZXNbLDFdPjAuNikKcGMyLndlaXJkID0gd2hpY2gocGMuZGF0YSRzY29yZXNbLDJdPCAtMC4xNSkKcG9pbnRzKHBjLmRhdGEkc2NvcmVzW3BjMS53ZWlyZCwgMV0sCiAgICAgICBwYy5kYXRhJHNjb3Jlc1twYzEud2VpcmQsIDJdLCBjb2wgPSAicmVkIiwgcGNoID0gMTkpCnBvaW50cyhwYy5kYXRhJHNjb3Jlc1twYzIud2VpcmQsIDFdLAogICAgICAgcGMuZGF0YSRzY29yZXNbcGMyLndlaXJkLCAyXSwgY29sID0gImdyZWVuIiwgcGNoID0gMTkpCmBgYAoKYGBge3J9CmNvbG9yID0gcmVwKCJncmF5IiwgZGltKGxha2UpWzFdKQpsd2QgPSByZXAoMSwgZGltKGxha2UpWzFdKQphbGZhID0gcmVwKDEuNSwgZGltKGxha2UpWzFdKQpjb2xvcltwYzEud2VpcmRdID0gInJlZCIKbHdkW3BjMS53ZWlyZF0gPSAxCmFsZmFbcGMxLndlaXJkXSA9IDEKcHBsb3QobGFrZSwgY29sID0gY29sb3IsIG1haW4gPSAiSGlnaCBQQzEiLCBsd2QgPSBsd2QsIGFsZmEgPSBhbGZhKQpgYGAKYGBge3J9CnBjMS53ZWlyZCA9IHdoaWNoKHBjLmRhdGEkc2NvcmVzWywxXTwgLSAwLjE1KQpjb2xvciA9IHJlcCgiZ3JheSIsIGRpbShsYWtlKVsxXSkKbHdkID0gcmVwKDEsIGRpbShsYWtlKVsxXSkKYWxmYSA9IHJlcCgwLjIsIGRpbShsYWtlKVsxXSkKY29sb3JbcGMxLndlaXJkXSA9ICJyZWQiCmx3ZFtwYzEud2VpcmRdID0gMQphbGZhW3BjMS53ZWlyZF0gPSAxCnBwbG90KGxha2UsIGNvbCA9IGNvbG9yLCBtYWluID0gIkxvdyBQQzEiLCBsd2QgPSBsd2QsIGFsZmEgPSBhbGZhKQpgYGAKUEMxIGhhcyBhIHNpbWlsYXIgaW50ZXJwcmV0YXRpb24gd2hlbiBjb21wYXJlZCB3aXRoIEthc3M6IGlmIFBDMSBpcyBsb3csIHRoZW4gdGhlIGZ1bmNpdG9ucyBhcmUgYWxtb3N0IGNvbnN0YW50LCB3aGlsZSBpZiBoaWdoLCB0aGV5IGhhdmUgcXVpdGUgaGlnaCBzcHJpbmcgcGVha3MuIFVuZm9ydHVuYXRlbHksIHRoZSBsaW5rIGJldHdlZW4gUEMxIGFuZCB0aGUgYXV0dW1uIHBlYWtzIHNlZW1zIHRvIGJlIGxvc3QsIGluZGVlZCB0aGUgcm9sZSBvZiB0aGUgYXZlYWdlIGFicyBvZiB2YXJpYWJpbGl0eSAoZmlyc3QgZGVyaXZhdGl2ZSkgaXMgbm90IHRoYXQgcmVsZXZhbnQgaW4gS29ydCdzIFBDMSB3aGVuIGNvbXBhcmVkIHdpdGggS2FzczoKYGBge3J9CnBhcihtZmNvbCA9IGMoMSwyKSkKYmFycGxvdChwcmluY29tcChmX2thc3MsIHNjb3Jlcz1UKSRsb2FkaW5nc1ssMV0sIHlsaW0gPSBjKC0xLCAxKSwgbWFpbj0iUEMxIC0gS2FzcyIpCmJhcnBsb3QocHJpbmNvbXAoZl9rb3J0LCBzY29yZXM9VCkkbG9hZGluZ3NbLDFdLCB5bGltID0gYygtMSwgMSksIG1haW49IlBDMSAtIEtvcnQiKQpgYGAKQWJvdXQgUEMyOgpgYGB7cn0KcGMyLndlaXJkID0gd2hpY2gocGMuZGF0YSRzY29yZXNbLDJdPiAwLjEpCmNvbG9yID0gcmVwKCJncmF5IiwgZGltKGxha2UpWzFdKQpsd2QgPSByZXAoMSwgZGltKGxha2UpWzFdKQphbGZhID0gcmVwKDAuMywgZGltKGxha2UpWzFdKQpjb2xvcltwYzIud2VpcmRdID0gInJlZCIKbHdkW3BjMi53ZWlyZF0gPSAyCmFsZmFbcGMyLndlaXJkXSA9IDEKcHBsb3QobGFrZSwgY29sID0gY29sb3IsIG1haW4gPSAiSGlnaCBQQzIiLCBsd2QgPSBsd2QsIGFsZmEgPSBhbGZhKQpgYGAKYGBge3J9CnBjMi53ZWlyZCA9IHdoaWNoKHBjLmRhdGEkc2NvcmVzWywyXTwgLTAuMTUpCmNvbG9yID0gcmVwKCJncmF5IiwgZGltKGxha2UpWzFdKQpsd2QgPSByZXAoMSwgZGltKGxha2UpWzFdKQphbGZhID0gcmVwKDAuMywgZGltKGxha2UpWzFdKQpjb2xvcltwYzIud2VpcmRdID0gInJlZCIKbHdkW3BjMi53ZWlyZF0gPSAyCmFsZmFbcGMyLndlaXJkXSA9IDEKcHBsb3QobGFrZSwgY29sID0gY29sb3IsIG1haW4gPSAiTG93IFBDMiIsIGx3ZCA9IGx3ZCwgYWxmYSA9IGFsZmEpCmBgYApIaWdoIFBDMjogdGhlbiB0aGUgY3VydmUgaGFzIG1haW5seSBvbmUgcGVhayB3aGljaCBoYXBwZW5zIGR1cmluZyBzcHJpbmcgKHNvbWUgZXhlcHRpb25zIHByZXNlbnQgcHJvbWluZW50IHBlYWtzIGV2ZW4gZHVyaW5nIGF1dHVtbiBhbmQgd2ludGVyKS4KTG93IFBDMjogbXVjaCBtb3JlIGRpZmZpY3VsdCB0byBpbnRlcnByZXQsIGJ1dCByZW1lbWVyIHRoYXQgdGhlIHNlY29uZCBQQyBleHBsYWlucyBtdWNoIGxlc3MgdmFyaWFiaWxpdHkgdGhhbiBQQzEuLi4KCiMjIE5hdXQKVGhlIGZhY3QgdGhhdCB0aGUgZGF0YSBjb2xsZWN0aW9uIHByb2Nlc3Mgb2YgS29ydCBhbmQgTmF1dCBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgb25lIG9mIEthc3MgbWF5IGJlIHRoZSByZWFzb24gZm9yIHRoZSBzaW1pbGFyaXRpZXMgYW1vbmcgdGhlIDIgZmlubmlzaCBsYWtlcyB3aGVuIGNvbXBhcmVkIHdpdGggdGhlIHN3ZWRpc2ggb25lLiBFdmVuIHRoZSBzY2F0dGVycGxvdCBvZiBQQzEgYW5kIFBDMiBhcmUgc2ltaWxhcjogCmBgYHtyfQpsYWtlID0gbmF1dApkX2xha2UgPSBkX25hdXQKZCA9IGZfbmF1dAoKcGMuZGF0YSA8LSBwcmluY29tcChmX25hdXQsIHNjb3Jlcz1UKQpwYXIobWZyb3cgPSBjKDEsMSkpCnBsb3QocHJpbmNvbXAoZl9uYXV0LCBzY29yZXM9VCkkc2NvcmVzWywxXSwgcHJpbmNvbXAoZl9uYXV0LCBzY29yZXM9VCkkc2NvcmVzWywyXSwgcGNoID0gMTkpCmBgYApUaGUgYmFycGxvdCBvZiB0aGUgbG9hZGluZ3Mgb2YgdGhlIFBDQSBmb3IgbmF1dCBpcyB2ZXJ5IHNpbWlsYXIgdG8gS2Fzcy4KTGV0J3MgbG9vayBhdCBQQzE6CmBgYHtyfQpwYzEud2VpcmQgPSB3aGljaChwYy5kYXRhJHNjb3Jlc1ssMV0+IDAuNSkKY29sb3IgPSByZXAoImdyYXkiLCBkaW0obGFrZSlbMV0pCmx3ZCA9IHJlcCgxLCBkaW0obGFrZSlbMV0pCmFsZmEgPSByZXAoMC4yLCBkaW0obGFrZSlbMV0pCmNvbG9yW3BjMS53ZWlyZF0gPSAicmVkIgpsd2RbcGMxLndlaXJkXSA9IDEKYWxmYVtwYzEud2VpcmRdID0gMQpwcGxvdChsYWtlLCBjb2wgPSBjb2xvciwgbWFpbiA9ICJIaWdoIFBDMSIsIGx3ZCA9IGx3ZCwgYWxmYSA9IGFsZmEpCmBgYApgYGB7cn0KcGMxLndlaXJkID0gd2hpY2gocGMuZGF0YSRzY29yZXNbLDFdPCAtMC4xOSkKY29sb3IgPSByZXAoImdyYXkiLCBkaW0obGFrZSlbMV0pCmx3ZCA9IHJlcCgxLCBkaW0obGFrZSlbMV0pCmFsZmEgPSByZXAoMC4xLCBkaW0obGFrZSlbMV0pCmNvbG9yW3BjMS53ZWlyZF0gPSAicmVkIgpsd2RbcGMxLndlaXJkXSA9IDIKYWxmYVtwYzEud2VpcmRdID0gMQpwcGxvdChsYWtlLCBjb2wgPSBjb2xvciwgbWFpbiA9ICJMb3cgUEMxIiwgbHdkID0gbHdkLCBhbGZhID0gYWxmYSkKYGBgClBDMSBoYXMgdGhlIGV4YWN0IHNhbWUgaW50ZXJwcmV0YXRpb24gYXMgbGFrZSBLYXNzLCBjb250cmFyeSB0byBLb3J0Li4uCkxldCdzIGxvb2sgYXQgUEMyOgpgYGB7cn0KcGMyLndlaXJkID0gd2hpY2gocGMuZGF0YSRzY29yZXNbLDJdPCAtMC4xNSkKY29sb3IgPSByZXAoImdyYXkiLCBkaW0obGFrZSlbMV0pCmx3ZCA9IHJlcCgxLCBkaW0obGFrZSlbMV0pCmFsZmEgPSByZXAoMC4zLCBkaW0obGFrZSlbMV0pCmNvbG9yW3BjMi53ZWlyZF0gPSAicmVkIgpsd2RbcGMyLndlaXJkXSA9IDIKYWxmYVtwYzIud2VpcmRdID0gMQpwcGxvdChsYWtlLCBjb2wgPSBjb2xvciwgbWFpbiA9ICJMb3cgUEMyIiwgbHdkID0gbHdkLCBhbGZhID0gYWxmYSkKYGBgCmBgYHtyfQpwYzIud2VpcmQgPSB3aGljaChwYy5kYXRhJHNjb3Jlc1ssMl0+IDAuMDkpCmNvbG9yID0gcmVwKCJncmF5IiwgZGltKGxha2UpWzFdKQpsd2QgPSByZXAoMSwgZGltKGxha2UpWzFdKQphbGZhID0gcmVwKDAuMywgZGltKGxha2UpWzFdKQpjb2xvcltwYzIud2VpcmRdID0gInJlZCIKbHdkW3BjMi53ZWlyZF0gPSAyCmFsZmFbcGMyLndlaXJkXSA9IDEKcHBsb3QobGFrZSwgY29sID0gY29sb3IsIG1haW4gPSAiSGlnaCBQQzIiLCBsd2QgPSBsd2QsIGFsZmEgPSBhbGZhKQpgYGAKUEMyIGhhcyBhIHNpbWlsYXIgaW50ZXJwcmV0YXRpb24gdG8gUEMyIG9mIEthc3MsIGJ1dCBhZ2FpbiBpdCBpcyBsZXNzIGV2aWRlbnQuCgpPdmVyYWxsOgoqIFBDMTogcGVha3MgaW50ZW5zaXR5ICh0aGUgbG93ZXIsIHRoZSBtb3JlIGNvbnN0YW50OyB0aGUgaGlnaGVyLCB0aGUgbW9yZSBldmlkZW50IHRoZSBwZWFrcyBhcmUpCiogUEMyOiAodGhlIGhpZ2hlciwgdGhlIG1vcmUgYSBmdW5jdGlvbiByZXNlbWJsZXMgdGhlIG1lYW47IHRoZSBsb3dlciwgdGhlIG1vcmUgc2hpZnRlZCB0aGUgc3ByaW5nIHBlYWsgaXMpCgpDT05TSURFUklORyBUSEFUIEtPUlQgQU5EIE5BVVQgQVJFIE1VQ0ggTU9SRSAiV0lHR0xZIiBUSEFOIEtBU1MgQU5EIFRIQVQgVEhFIFNFQ09ORCBQQyBFWFBMSU5TIE1VQ0ggTEVTUyBWQVJJQUJJTElUWSBUSEFOIFRIRSBGSVJTVCwgV0UgU1RJQ0sgV0lUSCBUSEUgSU5URVJQUkVUQVRJT04gT0YgUEMxIEFORCBQQ1MgQkFTRUQgT04gS0FTUy4gTU9SRU9WRVIsIFBDMiBPRiBLQVNTIEVYUExBSU5TIE1PUkUgVkFSSUFCSUxJVFkgVEhBTiBQQzIgT0YgS09SVCBBTkQgTkFVVDoKYGBge3J9CnBhcihtZnJvdz1jKDEsMSkpCnBsb3QoY3Vtc3VtKHByaW5jb21wKGZfa2Fzcywgc2NvcmVzPVQpJHNkZV4yKS9zdW0ocHJpbmNvbXAoZl9rYXNzLCBzY29yZXM9VCkkc2RlXjIpLCB0eXBlPSdiJywgYXhlcz1GLCB4bGFiPSdOdW1iZXIgb2YgY29tcG9uZW50cycsIGNvbCA9ICJibGFjayIsIHlsYWI9J0NvbnRyaWJ1dGlvbiB0byB0aGUgdG90YWwgdmFyaWFuY2UnLCB5bGltPWMoMCwxKSkKcG9pbnRzKGN1bXN1bShwcmluY29tcChmX2tvcnQsIHNjb3Jlcz1UKSRzZGVeMikvc3VtKHByaW5jb21wKGZfa29ydCwgc2NvcmVzPVQpJHNkZV4yKSwgdHlwZT0nYicsIHhsYWI9J051bWJlciBvZiBjb21wb25lbnRzJywgY29sID0gImJsdWUiKQpwb2ludHMoY3Vtc3VtKHByaW5jb21wKGZfbmF1dCwgc2NvcmVzPVQpJHNkZV4yKS9zdW0ocHJpbmNvbXAoZl9uYXV0LCBzY29yZXM9VCkkc2RlXjIpLCB0eXBlPSdiJywgeGxhYj0nTnVtYmVyIG9mIGNvbXBvbmVudHMnLCBjb2wgPSAicmVkIikKYXhpcygyLGF0PTA6MTAvMTAsbGFiZWxzPTA6MTAvMTApCmxlZ2VuZCgxLDAuNSwgbGVnZW5kPWMoImthc3MiLCAia29ydCIsICJuYXV0IiksIGNvbCA9IGMoImJsYWNrIiwgImJsdWUiLCAicmVkIiksIGx0eSA9IDEpCmJveCgpCmBgYAoKCgojIEhDCmBgYHtyfQpsYWtlID0ga2FzcwpkX2xha2UgPSBkX2thc3MKZCA9IGZfa2FzcwpwYy5kYXRhIDwtIHByaW5jb21wKGQsIHNjb3Jlcz1UKQpkZCA9IGRhdGEuZnJhbWUoIlBDMSI9cGMuZGF0YSRzY29yZXNbLDFdLCJQQzIiPXBjLmRhdGEkc2NvcmVzWywyXSkKZCA8LSBkaXN0KGRkLCBtZXRob2QgPSAnZXVjbGlkZWFuJykKaGMgPC0gaGNsdXN0KGQsIG1ldGhvZCA9ICJhdmVyYWdlIikKcGxvdChoYywgbGFiZWxzPUZBTFNFLCB4bGFiPSIiLCBzdWI9IiIpCnJlY3QuaGNsdXN0KGhjLCBrID0gNCwgYm9yZGVyID0gInJlZCIpCnJlY3QuaGNsdXN0KGhjLCBrID0gNSwgYm9yZGVyID0gImdyZWVuIikKcmVjdC5oY2x1c3QoaGMsIGsgPSA2LCBib3JkZXIgPSAiYmx1ZSIpCiMgdGFibGUoY3V0cmVlKGhjLCBrID0gMykpCmBgYApCeSBhcHBseWluZyBoY2x1c3QsIHdlIHNlZSB0aGF0IHRoZSByZXN1bHRzIGFyZSBub3QgdmVyeSBzYXRpc2Z5aW5nLCBzaW5jZSB0aGUgZ29hbCB3b3VsZCBiZSB0byBjbHVzdGVyIGNsaW1hdGUgdHlwZXMgYW5kIGFscmVhZHkgd2l0aCBrID0gMyB3ZSBzZWUgdGhhdCBvbmUgY2x1c3RlciBpcyBxdWl0ZSBzY2FyY2UgKGxlc3MgdGhhbiA2MCB5ZWFycyBiZWxvbmcgdG8gaXQpLiBNb3Jlb3ZlciB0aGUgY2x1c3RlcmluZyBpcyBjb21wbGV0ZWx5IGRyaXZlbiBieSB0aGUgZmlyc3QgUEM6CmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsMikpCnBsb3QoZGQsIGNvbCA9IGN1dHJlZShoYywgayA9IDMpLCBtYWluID0gImsgPSAzIikKcGxvdChkZCwgY29sID0gY3V0cmVlKGhjLCBrID0gNCksIG1haW4gPSAiayA9IDQiKQpwbG90KGRkLCBjb2wgPSBjdXRyZWUoaGMsIGsgPSA1KSwgbWFpbiA9ICJrID0gNSIpCnBsb3QoZGQsIGNvbCA9IGN1dHJlZShoYywgayA9IDYpLCBtYWluID0gImsgPSA2IikKcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKRXhhY3RseSB0aGUgc2FtZSByZXN1bHRzIGhvbGQgZm9yIHRoZSBvdGhlciAyIGZpbm5pc2ggbGFrZXMuIAoKIyBDb25mcm9tYWxDbHVzdGVyaW5nIApWZXJ5IGJhZAoKIyBLLW1lYW5zCltUaGUgZm9sbG93aW5nIHJlc3VsdHMgYXJlIGFwcGxpZWQgdG8gS2FzcywgYnV0IHRoZXkgY2FuIGJlIGdlbmVyYWxpemVkIHRvIHRoZSBmaW5uaXNoIGxha2VzXQpCeSBhcHBseWluZyBrLW1lYW5zIHdpdGggayA9IDMsIHdlIHNlZSBhIGNsYXIgaW1wcm92ZW1lbnQgd2hlbiBjb21wYXJlZCB0byBIQyAod2l0aCAzIGdyb3Vwcyk6CgpgYGB7cn0KbGFrZSA9IGthc3MKZF9sYWtlID0gZF9rYXNzCmQgPSBmX2thc3MKcGMuZGF0YSA8LSBwcmluY29tcChkLCBzY29yZXM9VCkKZGQgPSBkYXRhLmZyYW1lKCJQQzEiPXBjLmRhdGEkc2NvcmVzWywxXSwiUEMyIj1wYy5kYXRhJHNjb3Jlc1ssMl0pCmttID0ga21lYW5zKGRkLCBjZW50ZXJzID0gMywgbnN0YXJ0ID0gMjApCnRhYmxlKGttJGNsdXN0ZXIpCmBgYApTdGlsbCwgUEMxIHNlZW1zIHRvIGJlIHRoZSBtb3N0IHJlbGV2YW50IGNvbXBvbmVudDoKYGBge3J9CnBhcihtZnJvdyA9IGMoMywyKSkKcGxvdChkZCwgY29sID0ga21lYW5zKGRkLCBjZW50ZXJzID0gMywgbnN0YXJ0ID0gMjApJGNsdXN0ZXIsIG1haW4gPSAiayA9IDMiKQpwbG90KGRkLCBjb2wgPSBrbWVhbnMoZGQsIGNlbnRlcnMgPSA0LCBuc3RhcnQgPSAyMCkkY2x1c3RlciwgbWFpbiA9ICJrID0gNCIpCnBsb3QoZGQsIGNvbCA9IGttZWFucyhkZCwgY2VudGVycyA9IDUsIG5zdGFydCA9IDIwKSRjbHVzdGVyLCBtYWluID0gImsgPSA1IikKcGxvdChkZCwgY29sID0ga21lYW5zKGRkLCBjZW50ZXJzID0gNiwgbnN0YXJ0ID0gMjApJGNsdXN0ZXIsIG1haW4gPSAiayA9IDYiKQpwbG90KGRkLCBjb2wgPSBrbWVhbnMoZGQsIGNlbnRlcnMgPSA3LCBuc3RhcnQgPSAyMCkkY2x1c3RlciwgbWFpbiA9ICJrID0gNyIpCnBsb3QoZGQsIGNvbCA9IGttZWFucyhkZCwgY2VudGVycyA9IDgsIG5zdGFydCA9IDIwKSRjbHVzdGVyLCBtYWluID0gImsgPSA4IikKcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKSWYgd2UgdHJpZWQgdG8gaW5jbHVkZSB0aGUgdGhpcmQgUEMgaW4gdGhlIGFuYWx5c2lzLCB3ZSB3b3VsZCBvYnRhaW4gc2xpZ2hsdHkgZGlmZmVyZW50IHJlc3VsdHM6CmBgYHtyfQpkZGQgPSBkZApkZGQkUEMzID0gcGMuZGF0YSRzY29yZXNbLDNdCnBhcihtZnJvdyA9IGMoMywyKSkKcGxvdChkZGQsIGNvbCA9IGttZWFucyhkZGQsIGNlbnRlcnMgPSAzLCBuc3RhcnQgPSAyMCkkY2x1c3RlciwgbWFpbiA9ICJrID0gMyIpCnBsb3QoZGRkLCBjb2wgPSBrbWVhbnMoZGRkLCBjZW50ZXJzID0gNCwgbnN0YXJ0ID0gMjApJGNsdXN0ZXIsIG1haW4gPSAiayA9IDQiKQpwbG90KGRkZCwgY29sID0ga21lYW5zKGRkZCwgY2VudGVycyA9IDUsIG5zdGFydCA9IDIwKSRjbHVzdGVyLCBtYWluID0gImsgPSA1IikKcGxvdChkZGQsIGNvbCA9IGttZWFucyhkZGQsIGNlbnRlcnMgPSA2LCBuc3RhcnQgPSAyMCkkY2x1c3RlciwgbWFpbiA9ICJrID0gNiIpCnBsb3QoZGRkLCBjb2wgPSBrbWVhbnMoZGRkLCBjZW50ZXJzID0gNywgbnN0YXJ0ID0gMjApJGNsdXN0ZXIsIG1haW4gPSAiayA9IDciKQpwbG90KGRkZCwgY29sID0ga21lYW5zKGRkZCwgY2VudGVycyA9IDgsIG5zdGFydCA9IDIwKSRjbHVzdGVyLCBtYWluID0gImsgPSA4IikKcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKQnV0IHN0aWxsIFBDMSBpcyB0aGUgZHJpdmluZyBmYWN0b3IgYW5kLCBtb3Jlb3ZlciwgYnkgY29uc2lkZXJpbmcgMyBQQyB3ZSBhcmUgbm90IHJlYWxseSBwZXJmb3JtaW5nIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4gTW9yZW92ZXIsIHRoZSAzcmQgUEMgYWx3YXlzIGV4cGxhaW5zIGEgdmVyeSBsb3cgYW1vdW50IG9mIHZhcmlhYmlsaXR5OgpgYGB7cn0Kc3VtbWFyeShwYy5kYXRhKQpgYGAKRm9yIHRoaXMgcmVhc29uLCB3ZSBkZWNpZGUgbm90IHRvIGluY2x1ZGUgUEMzIGluIHRoZSBhbmFseXNpcy4KRmluYWxseSwgY2xlYXJseSBLbWVhbnMgaXMgbW9yZSBzYXRpc2Z5aW5nIHRoYW4gaGNsdXN0IHVubGVzcyBrIGlzIHNtYWxsIChyb3VnaGx5IDwgNikuCgojIEJhZ2dpbmdWb3Jvbm9pOgpLID0gMywgTCA9IDEwMCwgQiA9IDUwMCAoMiBtaW51dGVzKQohW10oL1VzZXJzL2Rpc2EvRGVza3RvcC9TY3JlZW5zaG90LnBuZykKCksgPSA2LCBMID0gMTAsIEIgPSA1MDAgKDQgbWludXRlcykKIVtdKC9Vc2Vycy9kaXNhL0Rlc2t0b3AvU2NyZWVuc2hvdDIucG5nKQoK